
#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using System;
using System.Collections.Generic;
#endregion

namespace MCGameEngine.Physic_Layer
{
    public class BasicObject
    {
        public float Mass;

        public BasicObject Parent;

        private Model model;

        public virtual Model Model
        {
            get
            {
                return model;
            }

            set
            {
                model = value;
                this.collision_sphere = new CollisionSphere(model);
            }
        }

        public CollisionSphere collision_sphere; 

        private const float MinimumAltitude = 650.0f;

        private Vector3 scale;

        public Vector3 Scale
        {
            get
            {
                return scale;
            }
            set
            {
                scale = value;
                World *= Matrix.CreateScale(scale);
            }
        }

        public Vector3 Rotation;

        public Vector3 Position;

        public Vector3 Direction;

        public Vector3 Up
        {
            get
            {
                return World.Up;
            }
        }

        public Matrix World;

        public BasicObject()
        {
            World = Matrix.Identity;
            Position = new Vector3(0, MinimumAltitude, 0);
            Rotation = Vector3.Zero;
            Scale = new Vector3(1, 1, 1);
            World = World * Matrix.CreateTranslation(Position);
            Direction = Vector3.Forward;
            World.Up = Vector3.Up;
            World.Right = Vector3.Right;

            Mass = float.PositiveInfinity;
        }

        public void Rotate(Vector3 angle)
        {
            Rotation += angle;
            Vector3 modelPosition = World.Translation;
            World.Translation = Vector3.Zero;
            
            Matrix rotationMatrix= Matrix.CreateRotationX(MathHelper.ToRadians(angle.X))
                                   * Matrix.CreateRotationY(MathHelper.ToRadians(angle.Y))
                                   * Matrix.CreateRotationZ(MathHelper.ToRadians(angle.Z));

            World *= rotationMatrix;

            Direction = Vector3.TransformNormal(Direction, rotationMatrix);
            
            World.Translation = modelPosition; 
        }

        public void RotateAround(Vector3 angle,Vector3 point)
        {
            World *= Matrix.CreateTranslation(-point);
            Matrix rotationMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(angle.X))
                                    * Matrix.CreateRotationY(MathHelper.ToRadians(angle.Y))
                                    * Matrix.CreateRotationZ(MathHelper.ToRadians(angle.Z));
            
            World *= rotationMatrix;
            Direction = Vector3.TransformNormal(Direction, rotationMatrix);
            
            World *= Matrix.CreateTranslation(point);
        }

        public void Move(Vector3 vector)
        {
            Position = vector;
            World = World * Matrix.CreateTranslation(Position);
            World.Translation = Position;
            World.Right = Vector3.Cross(Direction, World.Up);
        }

        public void Thrust(float step)
        {
            Position = Position + Direction * step;
            World = World * Matrix.CreateTranslation(Position);
            World.Translation = Position;
            World.Right = Vector3.Cross(Direction, World.Up);
        }

        public virtual void Update(GameTime gameTime)
        {
            if (Parent != null)
            {
                this.RotateAround(Parent.Rotation, Parent.Position);
                this.Move(Position + Parent.Position);
            }
        }

        public virtual void Draw(Camera camera)
        {
            if (model == null)
            {
                return;
            }

            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.World = transforms[mesh.ParentBone.Index] * World;
                    effect.View = camera.View;
                    effect.Projection = camera.Projection;
                }

                mesh.Draw();
            }
        }
    }
}
